﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Opennav.MapGenerator.Geometry
{
    public class Polygon
    {
        private int minLat = int.MaxValue, maxLat = int.MinValue, minLon = int.MaxValue, maxLon = int.MinValue;

        public Polygon(IList<Coordinate> coordinates)
        {
            this.Coordinates = coordinates.ToArray();
            foreach (Coordinate c in coordinates)
            {
                if (minLat > c.Latitude) minLat = c.Latitude;
                if (maxLat < c.Latitude) maxLat = c.Latitude;
                if (minLon > c.Longitude) minLon = c.Longitude;
                if (maxLon < c.Longitude) maxLon = c.Longitude;
            }
        }

        public Coordinate[] Coordinates { get; set; }

        public Coordinate GetRandom()
        {
            Random random = new Random();
            Coordinate result = null;
            do
            {
                result = new Coordinate(minLon + random.Next(maxLon - minLon), minLat + random.Next(maxLat - minLat));
            } while (!Contains(result));
            return result;
        }

        public bool Contains(Polygon other, int probes = 3)
        {
            for (int i = 0; i < probes; i++)
            {
                if (!Contains(other.GetRandom()))
                    return false;
            }
            return true;
        }

        public bool Contains(int longitude, int latitude)
        {
            return Contains(new Coordinate(longitude, latitude));
        }

        public bool Contains(Coordinate contains)
        {
            Coordinate c1, c2;
            bool inside = false;
            if (Coordinates == null)
                return inside;
            int length = Coordinates.Length;
            if (length < 3)
                return inside;
            Coordinate oldPoint = Coordinates[length - 1];
            for (int i = 0; i < length; i++)
            {
                Coordinate newPoint = Coordinates[i];
                if (newPoint.Longitude > oldPoint.Longitude)
                {
                    c1 = oldPoint;
                    c2 = newPoint;
                }
                else
                {
                    c1 = newPoint;
                    c2 = oldPoint;
                }
                if ((newPoint.Longitude < contains.Longitude) == (contains.Longitude <= oldPoint.Longitude)
                    && ((contains.Latitude - c1.Latitude) * (c2.Longitude - c1.Longitude)
                    < (c2.Latitude - c1.Latitude) * (contains.Longitude - c1.Longitude)))
                {
                    inside = !inside;
                }
                oldPoint = newPoint;
            }
            return inside;
        }

        public static IEnumerable<Polygon> ParseFromWays(IList<IList<Coordinate>> ways)
        {
            IList<IList<Coordinate>> _cache = new List<IList<Coordinate>>(ways);
            List<Polygon> polygons = new List<Polygon>();
            while (_cache.Count > 0)
            {
                List<Coordinate> polygon = new List<Coordinate>();
                IList<Coordinate> current = _cache.First();
                _cache.Remove(current);
                polygon.AddRange(current);
                bool isPolygon = true;
                Coordinate next = current.Last();
                Coordinate first = current.First();
                while (isPolygon && !(next.Equals(first)))
                {
                    if ((current = (from c in _cache where c.First().Equals(next) select c).FirstOrDefault()) != null)
                    {
                        next = current.Last();
                        for (int i = 1; i < current.Count; i++)
                            polygon.Add(current[i]);
                    }
                    else if ((current = (from c in _cache where c.Last().Equals(next) select c).FirstOrDefault()) != null)
                    {
                        next = current.First();
                        for (int i = current.Count - 1; i > 0; i--)
                            polygon.Add(current[i]);
                    }
                    else
                    {
                        isPolygon = false;
                        break;
                    }
                    _cache.Remove(current);
                }
                if (isPolygon && polygon.Count > 2)
                    polygons.Add(new Polygon(polygon));
            }
            return polygons;
        }
    }
}
